package com.hx.voice.recognition.gateway.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.hx.voice.recognition.api.vo.authority.PermissionInfo;
import com.hx.voice.recognition.api.vo.log.LogInfo;
import com.hx.voice.recognition.auth.client.config.ServiceAuthConfig;
import com.hx.voice.recognition.auth.client.config.UserAuthConfig;
import com.hx.voice.recognition.auth.client.jwt.ServiceAuthUtil;
import com.hx.voice.recognition.auth.client.jwt.UserAuthUtil;
import com.hx.voice.recognition.auth.common.util.jwt.IJWTInfo;
import com.hx.voice.recognition.common.context.BaseContextHandler;
import com.hx.voice.recognition.common.msg.BaseResponse;
import com.hx.voice.recognition.common.msg.auth.TokenForbiddenResponse;
import com.hx.voice.recognition.gateway.feign.ILogService;
import com.hx.voice.recognition.gateway.feign.IUserService;
import com.hx.voice.recognition.gateway.handler.RequestBodyRoutePredicateFactory;
import com.hx.voice.recognition.gateway.utils.DBLog;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @descripe：到达网管路由过滤器，统一校验应用是否授权
 * @company：南京华讯方舟通讯设备有限公司
 * @author：zhoujinbing
 * @time：2019/6/12 9:11
 * @version：V1.0
 */
@Configuration
@Slf4j
public class AccessGatewayFilter implements GlobalFilter {

    @Lazy
    @Autowired
    private IUserService userService;

    @Lazy
    @Autowired
    private ILogService logService;

    @Value("${gate.ingore.startWith}")
    private String startWith;

    @Autowired
    private UserAuthUtil userAuthUtil;

    @Autowired
    private ServiceAuthConfig serviceAuthConfig;

    @Autowired
    private UserAuthConfig userAuthConfig;

    @Autowired
    private ServiceAuthUtil serviceAuthUtil;

    private static final String GATE_WAY_PREFIX = "/api";


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("check token and user permission");

        //获取请求属性
        LinkedHashSet requiredAttribute = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        //获取本次请求
        ServerHttpRequest request = exchange.getRequest();
        //获取请求url
        String requestUri = request.getPath().pathWithinApplication().value();

        //截取路径
        if(requiredAttribute != null)
        {
            Iterator<URI> iterator = requiredAttribute.iterator();
            URI next = iterator.next();
            if(next.getPath().startsWith(GATE_WAY_PREFIX))
            {
                requestUri = next.getPath().substring(GATE_WAY_PREFIX.length());
            }
        }

        //获取请求方式
        final String method = request.getMethod().toString();
        BaseContextHandler.setToken(null);
        ServerHttpRequest.Builder mutate = request.mutate();

        //若是进行授权相关，以 auth/jwt相关则行
        if(isStartWith(requestUri))
        {
            ServerHttpRequest build = mutate.build();
            return chain.filter(exchange.mutate().request(build).build());
        }

        IJWTInfo user = null;
        try {
            user = getJWTUser(request,mutate);
        }catch (Exception e)
        {
            log.error("用户Token过期异常",e);
            return getVoidMono(exchange,new TokenForbiddenResponse("User Token Forbidden or Expired"));
        }

        List<PermissionInfo> permissionInfos = userService.getAllPermissionInfo();
        //判断资源是否启动权限约束
        Stream<PermissionInfo> stream = getPermissionIfs(requestUri,method,permissionInfos);
        List<PermissionInfo> result = stream.collect(Collectors.toList());
        PermissionInfo[] permissions = result.toArray(new PermissionInfo[]{});
        if(permissions.length > 0)
        {
            if(checkUserPermission(permissions,exchange,user))
            {
                return getVoidMono(exchange,new TokenForbiddenResponse("User Forbidden! Does not has Permission! "));
            }
        }

        //申请客户端密钥头
        mutate.header(serviceAuthConfig.getTokenHeader(),serviceAuthUtil.getClientToken());
        ServerHttpRequest req =  mutate.build();
        return chain.filter(exchange.mutate().request(req).build());
    }

    private boolean checkUserPermission(PermissionInfo[] permissions, ServerWebExchange exchange, IJWTInfo user) {
        List<PermissionInfo> permissionInfos = userService.getPermissionByUserName(user.getUniqueName());
        PermissionInfo current = null;
        for (PermissionInfo permission : permissions) {
            boolean isMatch = permissionInfos.parallelStream().anyMatch(new Predicate<PermissionInfo>() {
                @Override
                public boolean test(PermissionInfo permissionInfo) {
                    return permissionInfo.getCode().equals(permission.getCode());
                }
            });

            if(isMatch)
            {
                current = permission;
                break;
            }
        }
        if(current == null)
        {
            return true;
        }
        else
        {
            if(!RequestMethod.GET.toString().equals(current.getMethod()))
            {
                setCurrentUserInfoAndLog(exchange,user,current);
            }
            return false;
        }




    }

    /**
     * @description 保存用户操作信息
     * @method  setCurrentUserInfoAndLog
     * @param  [exchange, user, current]
     * @return void
     * @date: 2019/6/14 9:36
     * @author:zhoujinbing
     */
    private void setCurrentUserInfoAndLog(ServerWebExchange exchange, IJWTInfo user, PermissionInfo current) {
        String host = exchange.getRequest().getRemoteAddress().toString();
        LogInfo logInfo = new LogInfo(current.getMenu(), current.getName(), current.getUri(), new Date().getTime(), user.getId(), user.getName(), host, String.valueOf(exchange.getAttributes().get(RequestBodyRoutePredicateFactory.REQUEST_BODY_ATTR)));
        DBLog.getInstance().setLogService(logService).offerQueue(logInfo);
    }

    /**
     * @description 获取目标权限资源
     * @method  getPermissionIfs
     * @param  [requestUri, method, permissionInfos]
     * @return java.util.stream.Stream<com.hx.zjb.security.api.vo.authority.PermissionInfo>
     * @date: 2019/6/13 16:02
     * @author:zhoujinbing
     */
    private Stream<PermissionInfo> getPermissionIfs(String requestUri, String method, List<PermissionInfo> permissionInfos) {
        return permissionInfos.parallelStream().filter(new Predicate<PermissionInfo>() {
            @Override
            public boolean test(PermissionInfo permissionInfo) {
                String uri = permissionInfo.getUri();
                if(uri.indexOf("{") > 0)
                {
                    uri =  uri.replaceAll("\\{\\*\\}", "[a-zA-Z\\\\d]+");
                }
                String regEx = "^" + uri + "$";
                return (Pattern.compile(regEx).matcher(requestUri).find()) && method.equals(permissionInfo.getMethod());
            }
        });
    }

    /**
     * @description 返回jwt中的用户信息
     * @method  getJWTUser
     * @param  [request, mutate]
     * @return com.hx.zjb.security.auth.common.util.jwt.IJWTInfo
     * @date: 2019/6/13 14:49
     * @author:zhoujinbing
     */
    private IJWTInfo getJWTUser(ServerHttpRequest request, ServerHttpRequest.Builder mutate) throws Exception {

        List<String> strings = request.getHeaders().get(userAuthConfig.getTokenHeader());
        String authToken = null;
        if(CollUtil.isNotEmpty(strings))
        {
            authToken = strings.get(0);
        }

        if(StrUtil.isNotEmpty(authToken))
        {
            strings = request.getQueryParams().get("token");
            if(CollUtil.isNotEmpty(strings))
            {
                authToken = strings.get(0);
            }
        }
        //给客户端写头
        mutate.header(userAuthConfig.getTokenHeader(),authToken);
        BaseContextHandler.setToken(authToken);
        return userAuthUtil.getInfoFormToken(authToken);
    }

    /**
     * @description 网管抛异常
     * @method  getVoidMono
     * @param  [exchange, body]
     * @return reactor.core.publisher.Mono<java.lang.Void>
     * @date: 2019/6/13 14:47
     * @author:zhoujinbing
     */
    @NotNull
    private Mono<Void> getVoidMono(ServerWebExchange exchange, BaseResponse body)
    {
        exchange.getResponse().setStatusCode(HttpStatus.OK);
        byte[] bytes = JSONObject.toJSONString(body).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
        return exchange.getResponse().writeWith(Flux.just(buffer));
    }

    public boolean isStartWith(String requestUri)
    {
        for (String s : startWith.split(",")) {
            if(requestUri.startsWith(s))
            {
                return true;
            }
        }
        return false;
    }





}